﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.ApplicationModel.Resources.Core;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using System.Collections.Specialized;

namespace Scope8.Data
{
    /// <summary>
    /// Basisklasse für <see cref="SlideDataItem"/> und <see cref="SlideDataGroup"/>, die
    /// die gemeinsamen Eigenschaften für beides definiert. Dient zum Anzeigen auf den XAML Seiten.
    /// </summary>
    [Windows.Foundation.Metadata.WebHostHidden]
    public abstract class DataCommon : Scope8.Common.BindableBase
    {
        /// <summary>
        /// die BasisURL 
        /// </summary>
        private static Uri _baseUri = new Uri("ms-appx:///");

        /// <summary>
        /// Konstruktor der mit den angegebenen Eigenschaften eine Instanz der Klasse baut
        /// </summary>
        /// <param name="uniqueId">die eindeutige ID</param>
        /// <param name="title">der Titel</param>
        /// <param name="subtitle">der Untertitel</param>
        /// <param name="imagePath">die ImageUrl zu dem Bild, das verweendet werden soll als Rerpäsetation für das Object</param>
        /// <param name="description">die Beschreibung der Klasse</param>
        public DataCommon(String uniqueId, String title, String subtitle, String imagePath, String description)
        {
            this._uniqueId = uniqueId;
            this._title = title;
            this._subtitle = subtitle;
            this._description = description;
            this._imagePath = imagePath;
        }

        /// <summary>
        /// die eindeutige Schlüssel
        /// </summary>
        private string _uniqueId = string.Empty;

        /// <summary>
        /// Getter und Setter für den eindeitigen Schlüssel
        /// </summary>
        public string UniqueId
        {
            get { return this._uniqueId; }
            set { this.SetProperty(ref this._uniqueId, value); }
        }

        /// <summary>
        /// der Titel 
        /// </summary>
        private string _title = string.Empty;

        /// <summary>
        /// Getter und Setter für den Titel
        /// </summary>
        public string Title
        {
            get { return this._title; }
            set { this.SetProperty(ref this._title, value); }
        }

        /// <summary>
        /// Subtitel
        /// </summary>
        private string _subtitle = string.Empty;

        /// <summary>
        /// /// Getter und Setter für den Subtitel
        /// </summary>
        public string Subtitle
        {
            get { return this._subtitle; }
            set { this.SetProperty(ref this._subtitle, value); }
        }

        /// <summary>
        /// die Beschreibung
        /// </summary>
        private string _description = string.Empty;

        /// <summary>
        /// /// Getter und Setter für die Beschreibung
        /// </summary>
        public string Description
        {
            get { return this._description; }
            set { this.SetProperty(ref this._description, value); }
        }

        /// <summary>
        /// das Bild, das das Obejct repäsentiert
        /// </summary>
        private ImageSource _image = null;
        /// <summary>
        /// die Image URL zum BIld
        /// </summary>
        private String _imagePath = null;

        /// <summary>
        /// /// Getter und Setter für das Bild, das das Obejct repäsentiert
        /// </summary>
        public ImageSource Image
        {
            get
            {
                if (this._image == null && this._imagePath != null)
                {
                    this._image = new BitmapImage(new Uri(DataCommon._baseUri, this._imagePath));
                }
                return this._image;
            }

            set
            {
                this._imagePath = null;
                this.SetProperty(ref this._image, value);
            }
        }

        /// <summary>
        /// Setzt die Ulr für das Bild
        /// </summary>
        /// <param name="path"></param>
        public void SetImage(String path)
        {
            this._image = null;
            this._imagePath = path;
            this.OnPropertyChanged("Image");
        }

        /// <summary>
        /// Eine String repräsentation des Objectes
        /// </summary>
        /// <returns>Liefert den eine Stringrepräsentation des Objektes zurück</returns>
        public override string ToString()
        {
            return this.Title;
        }
    }

    /// <summary>
    /// Generisches Elementdatenmodell; Stellt ein Element dar, das zusätzlich noch einen Slide als Objekt hat. 
    /// </summary>
    public class SlideDataItem : DataCommon
    {
        /// <summary>
        /// Konstruktor der ein Objekt der Klasse instantiiert.
        /// </summary>
        /// <param name="uniqueId">die eindeutige ID; benötigt für die Gruppe</param>
        /// <param name="title">der Titel</param>
        /// <param name="subtitle">der Subtitel</param>
        /// <param name="imagePath">die URL des Bildes</param>
        /// <param name="description">die Beschreibung</param>
        /// <param name="content">der Inhalt</param>
        /// <param name="group">die Gruppe</param>
        public SlideDataItem(String uniqueId, String title, String subtitle, String imagePath, String description, String content, SlideDataGroup group)
            : base(uniqueId, title, subtitle, imagePath, description)
        {
            this._content = content;
            this._group = group;
        }

        /// <summary>
        /// das Slide, das das Objekt beinhaltet kann
        /// </summary>
        private Slide anSlideEntry;

        /// <summary>
        /// die Getter und Setter, die für dasselbe Assembly zur Verfügung steht.
        /// </summary>
        internal Slide AnSlideEntry
        {
            get { return anSlideEntry; }
            set { anSlideEntry = value; }
        }

        /// <summary>
        /// der Inhalt
        /// </summary>
        private string _content = string.Empty;
        public string Content
        {
            get { return this._content; }
            set { this.SetProperty(ref this._content, value); }
        }

        /// <summary>
        /// die zugeörige Gruppe zum Objekt
        /// </summary>
        private SlideDataGroup _group;

        /// <summary>
        /// Getter und Setter liefert die Gruppe oder setzt diese.
        /// </summary>
        public SlideDataGroup Group
        {
            get { return this._group; }
            set { this.SetProperty(ref this._group, value); }
        }
    }

    /// <summary>
    /// Repräsentiert eine Gruppe mit Elementen. Sobald sich in den Elementen etwas ändert. 
    /// </summary>
    public class SlideDataGroup : DataCommon
    {
        /// <summary>
        /// Konstruktor für die Klasse; erzeugt ein Objekt der Klasse
        /// </summary>
        /// <param name="uniqueId">die eindeutige ID; benötigt für die Gruppe</param>
        /// <param name="title">der Titel</param>
        /// <param name="subtitle">der Subtitel</param>
        /// <param name="imagePath">die URL des Bildes</param>
        /// <param name="description">die Beschreibung der Gruppe</param>
        public SlideDataGroup(String uniqueId, String title, String subtitle, String imagePath, String description)
            : base(uniqueId, title, subtitle, imagePath, description)
        {
            Items.CollectionChanged += ItemsCollectionChanged;
        }

        /// <summary>
        /// Wird aufgerufen, falls sich die Items bzw. die Eigenschaften der items ändert.
        /// </summary>
        /// <param name="sender">der SEnder</param>
        /// <param name="e">das Ereignis</param>
        private void ItemsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            // Stellt aus zwei Gründen eine Teilmenge der vollständigen Elementauflistung zum Binden aus einer GroupedItemsPage
            // bereit: GridView virtualisiert keine großen Elementauflistungen, und das
            // Benutzererlebnis wird beim Navigieren durch Gruppen mit einer großen Anzahl von Elementen
            // verbessert.
            //
            // Maximal zwölf Elemente werden angezeigt, da dies zu gefüllten Rasterspalten führt,
            // unabhängig davon, ob 1, 2, 3, 4 oder 6 Zeilen angezeigt werden.

            //Wie viele Elemente sollen in der Gruppe angezeigt werden
            int count = 1;

            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    if (e.NewStartingIndex < count)
                    {
                        TopItems.Insert(e.NewStartingIndex,Items[e.NewStartingIndex]);
                        if (TopItems.Count > count)
                        {
                            TopItems.RemoveAt(count);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Move:
                    if (e.OldStartingIndex < count && e.NewStartingIndex < count)
                    {
                        TopItems.Move(e.OldStartingIndex, e.NewStartingIndex);
                    }
                    else if (e.OldStartingIndex < count)
                    {
                        TopItems.RemoveAt(e.OldStartingIndex);
                        TopItems.Add(Items[count -1]);
                    }
                    else if (e.NewStartingIndex < count)
                    {
                        TopItems.Insert(e.NewStartingIndex, Items[e.NewStartingIndex]);
                        TopItems.RemoveAt(count);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    if (e.OldStartingIndex < count)
                    {
                        TopItems.RemoveAt(e.OldStartingIndex);
                        if (Items.Count >= count)
                        {
                            TopItems.Add(Items[count - 1]);
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    if (e.OldStartingIndex < count)
                    {
                        TopItems[e.OldStartingIndex] = Items[e.OldStartingIndex];
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:
                    TopItems.Clear();
                    while (TopItems.Count < Items.Count && TopItems.Count < count)
                    {
                        TopItems.Add(Items[TopItems.Count]);
                    }
                    break;
            }
        }

        /// <summary>
        /// die Liste der Items
        /// </summary>
        private ObservableCollection<SlideDataItem> _items = new ObservableCollection<SlideDataItem>();

        /// <summary>
        /// getter und Setter der Liste
        /// </summary>
        public ObservableCollection<SlideDataItem> Items
        {
            get { return this._items; }
        }

        /// <summary>
        /// die Items, die sich auf der ersten Seite angezeigt werden soll
        /// </summary>
        private ObservableCollection<SlideDataItem> _topItem = new ObservableCollection<SlideDataItem>();

        /// <summary>
        /// die Items, die sich auf der ersten Seite angezeigt werden soll
        /// </summary>
        public ObservableCollection<SlideDataItem> TopItems
        {
            get {return this._topItem; }
        }
    }

    /// <summary>
    /// Erstellt eine Auflistung von Gruppen und Elementen mit hartcodiertem Inhalt.
    /// 
    /// SampleDataSource wird üblicherweise mit Platzhalterdaten und nicht mit Daten der Liveproduktion
    /// initialisiert, die Beispieldaten werden zur Entwurfszeit und zur Laufzeit bereitgestellt.
    /// </summary>
    public sealed class SlideDataSource
    {
        /// <summary>
        /// die Datenquelle
        /// </summary>
        private static SlideDataSource _sampleDataSource = new SlideDataSource();

        /// <summary>
        /// alle Gruppen der Datascource
        /// </summary>
        private ObservableCollection<SlideDataGroup> _allGroups = new ObservableCollection<SlideDataGroup>();

        /// <summary>
        /// Getter und Setter für alle Gruppen
        /// </summary>
        public ObservableCollection<SlideDataGroup> AllGroups
        {
            get { return this._allGroups; }
        }

        /// <summary>
        /// Liefert die Gruppe zurück mit der eindeutigen ID
        /// </summary>
        /// <param name="uniqueId">die eindeutige ID</param>
        /// <returns>Liefert die Gruppen mit der eindeutigen ID zurück</returns>
        public static IEnumerable<SlideDataGroup> GetGroups(string uniqueId)
        {
            if (!uniqueId.Equals("AllGroups")) throw new ArgumentException("Only 'AllGroups' is supported as a collection of groups");
            
            return _sampleDataSource.AllGroups;
        }

        /// <summary>
        /// Liefert die Gruppe mit der eindeutigen Id zurück
        /// </summary>
        /// <param name="uniqueId">die eindeutige ID</param>
        /// <returns>Liefert die eindeutige ID</returns>
        public static SlideDataGroup GetGroup(string uniqueId)
        {
            // Einfache lineare Suche ist bei kleinen DataSets akzeptabel.
            var matches = _sampleDataSource.AllGroups.Where((group) => group.UniqueId.Equals(uniqueId));
            if (matches.Count() == 1) return matches.First();
            return null;
        }

        /// <summary>
        /// Liefet ein Element mit der eindeutigen ID zurück
        /// </summary>
        /// <param name="uniqueId">die eindeutige ID des Elements</param>
        /// <returns>das Element mit der eindeutigen ID</returns>
        public static SlideDataItem GetItem(string uniqueId)
        {
            // Einfache lineare Suche ist bei kleinen DataSets akzeptabel.
            var matches = _sampleDataSource.AllGroups.SelectMany(group => group.Items).Where((item) => item.UniqueId.Equals(uniqueId));
            if (matches.Count() == 1) return matches.First();
            return null;
        }

        /// <summary>
        /// Erstellt 2 Gruppen
        /// </summary>
        public SlideDataSource()
        {
            var group1 = new SlideDataGroup("Charite",
                                               "Charite:",
                                               "",
                                               "Images/charite.jpg",
                                               "Beinhaltet alle Bilder der Charite Server");

            List<Slide> listCharite = ImageServer.StaticAvaibleSlides[0];

            for (int i = 0; i < listCharite.Count; i++)
            {
                Slide anEntry = listCharite[i];               
                //Convert.ToString(i)
                SlideDataItem newItem = new SlideDataItem("Charite" + i,
                                                anEntry.getPicName(),
                                                "",
                                                anEntry.getfullUrl() + anEntry.getThumbNails(),
                                                "Height: " + anEntry.getHeight() + "\n" + "Width: " + anEntry.getWidth(),
                                                "",
                                                group1);
                newItem.AnSlideEntry = anEntry;
                group1.Items.Add(newItem);
            }
            this.AllGroups.Add(group1);

            
            var group2 = new SlideDataGroup("Aperio",
                                   "Aperio:",
                                   "",
                                   "Images/aperio.jpg",
                                   "Beinhaltet alle Bilder der Aperio Server");

            List<Slide> listAperio = ImageServer.StaticAvaibleSlides[1];

            for (int i = 0; i < listAperio.Count; i++)
            {
                Slide anEntry = listAperio[i];

                SlideDataItem newItem = new SlideDataItem("Aperio" + i,
                                                anEntry.getPicName(),
                                                "",
                                                anEntry.getfullUrl() + anEntry.getThumbNails(),
                                                "Height: " + anEntry.getHeight() + "\n" + "Width: " + anEntry.getWidth(),
                                                "",
                                                group2);
                newItem.AnSlideEntry = anEntry;
                group2.Items.Add(newItem);
            }
            this.AllGroups.Add(group2);
           
        }
    }
}
